/*
 * UAE - The Un*x Amiga Emulator
 *
 * CD32 FMV cartridge
 *
 * Copyright 2008-2010 Toni Wilen
 *
 */

#include "sysconfig.h"
#include "cd32_fmv.h"
#include "uae.h"
#include "rommgr.h"
#include "zfile.h"
#include "newcpu.h"

/*
   0x200000 - 0x23FFFF ROM
   0x240000 io/status (single word register?)
   0x2500xx L64111 audio decoder (word registers)
   0x2700xx CL450 video decoder (word registers)
   0x280000 - 0x2FFFFF RAM
 */

#define IO_BASE 0x040000
#define L64111_BASE 0x050000
#define CL450_BASE 0x070000
#define VRAM_BASE 0x080000

#define BANK_MASK 0x0F0000

#define IO_IRQ_L641111 0x4000
#define IO_IRQ_CL450 0x8000

//  L64111 registers (from datasheet)
#define A_DATA 0            //0
#define A_CONTROL1 1        //2
#define A_CONTROL2 2        //4
#define A_CONTROL3 3        //6
#define A_INT1 4            //8
#define A_INT2 5            //10
#define A_TCR 6             //12
#define A_TORH 7            //14
#define A_TORL 8            //16
#define A_PARAM1 9          //18
#define A_PARAM2 10         //20
#define A_PARAM3 11         //22
#define A_PRESENT1 12       //24
#define A_PRESENT2 13       //26
#define A_PRESENT3 14       //28
#define A_PRESENT4 15       //30
#define A_PRESENT5 16       //32
#define A_FIFO 17           //34
#define A_CB_STATUS 18      //36
#define A_CB_WRITE 19       //38
#define A_CB_READ 20        //40

static int fmv_mask;
static byte* rom;
static int rom_size = 262144;
static uae_ptr fmv_start = 0x00200000;
static int fmv_size = 1048576;

static ushort l64111regs[32];
static ushort l64111intmask1, l64111intmask2, l64111intstatus1, l64111intstatus2;
static ushort io_reg;

static int isdebug(uae_ptr addr)
{
    #if CD32_FMV_DEBUG_DEF > 2
    if (M68K_GETPC >= 0x200100)
        return 1;
    return 0;
    #endif

    #if CD32_FMV_DEBUG_DEF == 2
    if (M68K_GETPC >= 0x200100 && (addr & fmv_mask) >= VRAM_BASE)
        return 1;
    return 0;
    #endif

    return 0;
}

static byte io_bget(uae_ptr addr)
{
    addr &= 0xffff;
    Logger::Write(L"FMV: IO byte read access %08x!\n", addr);
    return 0;
}
static ushort io_wget(uae_ptr addr)
{
    addr &= 0xffff;
    if (addr != 0)
        return 0;
    return io_reg;
}
static void io_bput(uae_ptr addr, byte v)
{
    addr &= 0xffff;
    Logger::Write(L"FMV: IO byte write access %08x!\n", addr);
}
static void io_wput(uae_ptr addr, ushort v)
{
    addr &= 0xffff;
    if (addr != 0)
        return;
    Logger::Write(L"FMV: IO=%04x\n", v);
    io_reg = v;
}

static byte l64111_bget(uae_ptr addr)
{
    Logger::Write(L"FMV: L64111 byte read access %08x!\n", addr);
    return 0;
}
static void l64111_bput(uae_ptr addr, byte v)
{
    Logger::Write(L"FMV: L64111 byte write access %08x!\n", addr);
}

static ushort l64111_wget(uae_ptr addr)
{
    addr >>= 1;
    addr &= 31;
    #if CD32_FMV_DEBUG_DEF
    Logger::Write(L"FMV: L64111 read reg %d -> %04x\n", addr, l64111regs[addr]);
    #endif
    if (addr == 4)
        return l64111intstatus1;
    if (addr == 5)
        return l64111intstatus1;

    return l64111regs[addr];
}
static void l64111_wput(uae_ptr addr, ushort v)
{
    addr >>= 1;
    addr &= 31;

    #if CD32_FMV_DEBUG_DEF
    Logger::Write(L"FMV: L64111 write reg %d = %04x\n", addr, v);
    #endif

    if (addr == 4)
    {
        l64111intmask1 = v;
        return;
    }
    if (addr == 5)
    {
        l64111intmask2 = v;
        return;
    }

    l64111regs[addr] = v;
}

static byte cl450_bget(uae_ptr addr)
{
    addr &= 0xff;
    Logger::Write(L"FMV: CL450 byte read access %08x!\n", addr);
    return 0;
}
static ushort cl450_wget(uae_ptr addr)
{
    addr &= 0xff;
    addr >>= 1;
    Logger::Write(L"FMV: CL450 read reg %d\n", addr);
    return 0;
}
static void cl450_bput(uae_ptr addr, byte v)
{
    addr &= 0xff;
    Logger::Write(L"FMV: CL450 byte write access %08x!\n", addr);
}
static void cl450_wput(uae_ptr addr, ushort v)
{
    addr &= 0xff;
    Logger::Write(L"FMV: CL450 write reg %d = %04x\n", addr, v);
}

static byte romram_bget(uae_ptr addr)
{
    #if CD32_FMV_DEBUG_DEF
    if (isdebug(addr))
        Logger::Write(L"romram_bget %08X PC=%08X\n", addr, M68K_GETPC);
    #endif
    if (addr >= IO_BASE && addr < VRAM_BASE)
        return 0;
    return rom[addr];
}
static ushort romram_wget(uae_ptr addr)
{
    #if CD32_FMV_DEBUG_DEF
    if (isdebug(addr))
        Logger::Write(L"romram_wget %08X PC=%08X\n", addr, M68K_GETPC);
    #endif
    if (addr >= IO_BASE && addr < VRAM_BASE)
        return 0;
    return (rom[addr] << 8) | (rom[addr + 1] << 0);
}
static void ram_bput(uae_ptr addr, byte v)
{
    if (addr < VRAM_BASE)
        return;
    rom[addr] = v;
    if (isdebug(addr))
    {
        Logger::Write(L"ram_bput %08X=%02X PC=%08X\n", addr, v & 0xff, M68K_GETPC);
    }
}
static void ram_wput(uae_ptr addr, ushort v)
{
    if (addr < VRAM_BASE)
        return;
    rom[addr + 0] = v >> 8;
    rom[addr + 1] = v >> 0;
    if (isdebug(addr))
    {
        Logger::Write(L"ram_wput %08X=%04X PC=%08X\n", addr, v & 0xffff, M68K_GETPC);
    }
}

static uint REGPARAM2 fmv_wget(uae_ptr addr)
{
    uint v;
    addr -= fmv_start & fmv_mask;
    addr &= fmv_mask;
    int mask = addr & BANK_MASK;
    if (mask == L64111_BASE)
        v = l64111_wget(addr);
    else if (mask == CL450_BASE)
        v = cl450_wget(addr);
    else if (mask == IO_BASE)
        v = io_wget(addr);
    else
        v = romram_wget(addr);

    #if CD32_FMV_DEBUG_DEF
    if (isdebug(addr))
        Logger::Write(L"fmv_wget %08X=%04X PC=%08X\n", addr, v, M68K_GETPC);
    #endif
    return v;
}

static uint REGPARAM2 fmv_lget(uae_ptr addr)
{
    uint v;
    v = (fmv_wget(addr) << 16) | (fmv_wget(addr + 2) << 0);
    #if CD32_FMV_DEBUG_DEF
    if (isdebug(addr))
        Logger::Write(L"fmv_lget %08X=%08X PC=%08X\n", addr, v, M68K_GETPC);
    #endif
    return v;
}

static uint REGPARAM2 fmv_bget(uae_ptr addr)
{
    uint v;
    addr -= fmv_start & fmv_mask;
    addr &= fmv_mask;
    int mask = addr & BANK_MASK;
    if (mask == L64111_BASE)
        v = l64111_bget(addr);
    else if (mask == CL450_BASE)
        v = cl450_bget(addr);
    else if (mask == IO_BASE)
        v = io_bget(addr);
    else
        v = romram_bget(addr);
    return v;
}

static void REGPARAM2 fmv_wput(uae_ptr addr, uint w)
{
    addr -= fmv_start & fmv_mask;
    addr &= fmv_mask;
    #if CD32_FMV_DEBUG_DEF
    if (isdebug(addr))
        Logger::Write(L"fmv_wput %04X=%04X PC=%08X\n", addr, w & 65535, M68K_GETPC);
    #endif
    int mask = addr & BANK_MASK;
    if (mask == L64111_BASE)
        l64111_wput(addr, w);
    else if (mask == CL450_BASE)
        cl450_wput(addr, w);
    else if (mask == IO_BASE)
        io_wput(addr, w);
    else
        ram_wput(addr, w);
}

static void REGPARAM2 fmv_lput(uae_ptr addr, uint w)
{
    #if CD32_FMV_DEBUG_DEF
    if (isdebug(addr))
        Logger::Write(L"fmv_lput %08X=%08X PC=%08X\n", addr, w, M68K_GETPC);
    #endif
    fmv_wput(addr + 0, w >> 16);
    fmv_wput(addr + 2, w >> 0);
}

extern addrbank fmv_bank;

static void REGPARAM2 fmv_bput(uae_ptr addr, uint w)
{
    addr -= fmv_start & fmv_mask;
    addr &= fmv_mask;
    int mask = addr & BANK_MASK;
    if (mask == L64111_BASE)
        l64111_bput(addr, w);
    else if (mask == CL450_BASE)
        cl450_bput(addr, w);
    else if (mask == IO_BASE)
        io_bput(addr, w);
    else
        ram_bput(addr, w);
}

static uint REGPARAM2 fmv_wgeti(uae_ptr addr)
{
    uint v = 0;
    byte* m;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    addr -= fmv_start & fmv_mask;
    addr &= fmv_mask;
    m = rom + addr;
    if (addr < rom_size)
        return do_get_mem_word((ushort*)m);
    #if CD32_FMV_DEBUG_DEF
    Logger::Write(L"fmv_wgeti %08X %08X PC=%08X\n", addr, v, M68K_GETPC);
    #endif
    return v;
}

static uint REGPARAM2 fmv_lgeti(uae_ptr addr)
{
    uint v = 0;
    byte* m;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    addr -= fmv_start & fmv_mask;
    addr &= fmv_mask;
    m = rom + addr;
    if (addr < rom_size)
        return do_get_mem_long((uint*)m);
    #if CD32_FMV_DEBUG_DEF
    Logger::Write(L"fmv_lgeti %08X %08X PC=%08X\n", addr, v, M68K_GETPC);
    #endif
    return v;
}

static int REGPARAM2 fmv_check(uae_ptr addr, uint size)
{
    addr -= fmv_start & fmv_mask;
    addr &= fmv_mask;
    return (addr + size) <= fmv_size;
}

static byte* REGPARAM2 fmv_xlate(uae_ptr addr)
{
    addr -= fmv_start & fmv_mask;
    addr &= fmv_mask;
    return rom + addr;
}

/*static*/ addrbank fmv_bank = {
    fmv_lget, fmv_wget, fmv_bget,
    fmv_lput, fmv_wput, fmv_bput,
    fmv_xlate, fmv_check, nullptr, L"CD32 FMV module",
    fmv_lgeti, fmv_wgeti, ABFLAG_ROM | ABFLAG_IO
};

void cd32_fmv_init(uae_ptr start)
{
    int ids[] = { 23, -1 };
    struct romlist* rl = getromlistbyids(ids);
    struct romdata* rd;
    struct zfile* z;

    Logger::Write(L"CD32 FMV mapped @$%lx\n", start);
    if (start != fmv_start)
        return;
    if (!rl)
        return;
    rd = rl->rd;
    z = read_rom(&rd);
    if (z)
    {
        Logger::Write(L"CD32 FMV ROM %d.%d\n", rd->ver, rd->rev);
        rom = mapped_malloc(fmv_size, L"fast");
        if (rom)
            zfile_fread(rom, rd->size, 1, z);
        zfile_fclose(z);
    }
    fmv_mask = fmv_size - 1;
    fmv_bank.baseaddr = rom;
    map_banks(&fmv_bank, start >> 16, fmv_size >> 16, 0);
}